首先了解this

如果想用好这几个方法,需要先了解this被调用方式不同而导致值不同的各种情况,然后就会认识到使用这几个方法的原因在哪里。(可以指定this的值)
全局上下文中this指向全局对象,函数上下文this取决于被调用的方式

例:

  • 在非严格模式下,全局调用函数this默认指向全局(window)
var a = 3

function exp(){
  var a = 4
  console.log(this.a)
}

var b = exp()
// 输出3
  • 在严格模式下,因为this为其进入上下文时的值,所以为undefined

function exp(){
 "use strict"
  return this
}

console.log(exp() === undefined)
// 输出 true

下面分析各个调用场合下this的值

作为函数被直接调用

上面已经写出了这种情况,值得注意的是,我们往往并不需要this值为window

作为方法被调用

作为方法被调用时,this指向方法所在的对象上
例:

var exp =  {
  obj: function context() {
     var text = "hello"
     return this 
  }
}

console.log(exp.obj() === exp)
var a = exp.obj()
console.log(a === exp)

var b = exp.obj
console.log(b() === window) //true,,注意这里当对象的方法被全局调用后this是b的this,则是window

//均输出 true

作为构造函数被调用

我们知道构造函数创建一个对象的过程

  1. 创建新对象
  2. 新对象作为this指向的对象
  3. 为新对象添加方法、属性、、并返回对象

需要注意的地方:构造函数返回一个非对象类型时,创建新对象时并不妨碍this的使用,也会返回新创建的对象。但是当构造函数显示返回一个对象时就会将这个对象赋值给变量,this的使用则无效。

function Fn (){
  this.obj = function() {
    return this
  }
}

let a = new Fn()
console.log(a.obj() === Fn) // false
console.log(a.obj() === a) //true

let newObj = {
  name: "小明"
}

function reObj (){
  this.name = "康康"
  return newObj
}

let b = new reObj()
console.log(b.name) //小明,返回的对象是newObj

进入正题,在这么多变化中随时都可能出错,所以call()、apply()、bind()就提供了一个可以指定this的方式

方法的使用

  1. call()

    这个方法接受多个参数,第一个参数是指定的this值,剩下的都是调用的函数的参数列表
    fn.call(this, arg1, arg2, ...);
    如果第一个参数需要是对象,如果传入了数字、字符串、布尔值的话this会指向该原始值的自动包装对象

    function f(){
          console.log(this)
          console.log(arguments)
      }
      f.call() // window
      f.call({name:'小明'}) // {name: '小明'}, []
      f.call({name:'小红'},1) // {name: '小红'}, [1]
      f.call({name:'康康'},1,2) // {name: '康康'}, [1,2]
  2. apply()

    apply() 与call()区别在于第二个参数接受的是一个包含多个参数的数组,对于一些方法需要传入的参数不能使数组,可以使用apply()调用函数使其可以使用数组作为参数。

    var a = [1,2,3,4,5,6,7,8,9]
    sum.apply(null,a)
    //将参数a全都传入,它会把参数作为数组传入。
    
    //求数组的最大元素
    Math.max.apply(null,[1,2,6]) // 6

    很多使用场景都可以被es6里的扩展运算符替代

  3. bind()

    bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值.

    this.name = "大牛"
    
    let obj = {
      name: "康康",
      age: 18,
      city:"上海"
    }
    
    let newObj =  {
      name: "小明",
      sayName: function() {
        console.log(this.name)
      }
    }
    
    newObj.sayName()// 小明
    
    let a = newObj.sayName.bind(obj)
    a() //康康
    
    let b = newObj.sayName
    b() //大牛
  4. 箭头函数
    这里说一下箭头函数,因为箭头函数没有this,所以会根据作用域链进行寻找this,这也衍生了很多用法,比如在setTimeout里经常出现的上下文(作用域)问题,如果不使用箭头函数,在函数运行时作用域就变成了全局,使用箭头函数会使函数里用到的this绑定在setTimeout的作用域上

    var timer = {
    fn1() {
        setTimeout(function(){
            console.log(this)
        }, 10)
    },
    fn2() {
        setTimeout(()=>{
            console.log(this)
        },20)
    },
    fn3: ()=> {
        setTimeout(()=>{
            console.log(this)
        },30)        
    }
     }
     timer.fn1() //window
     timer.fn2() // timer
     timer.fn3() //window
     
     //第一个在执行时是在全局调用,相当于 fn1.call(undefined)
     // 第二个使用箭头函数自身没this,使this 指向了timer
     // 第三个自身没this的情况下,根据箭头函数的规则找到了最外层全局(window)

戴西西
7 声望1 粉丝